package com.mkx.core.ml;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Redis分布式锁机器学习预测器
 * 使用历史数据和简单的预测算法来优化重试间隔
 */
public class RedisLockMLPredictor {

    private static final Logger logger = LoggerFactory.getLogger(RedisLockMLPredictor.class);
    private static final RedisLockMLPredictor INSTANCE = new RedisLockMLPredictor();
    
    // 每个锁键的历史数据
    private final ConcurrentHashMap<String, LockKeyHistory> lockKeyHistoryMap = new ConcurrentHashMap<>();
    
    // 全局历史数据
    private final GlobalLockHistory globalHistory = new GlobalLockHistory();
    
    // 是否启用机器学习预测
    private boolean enabled = true;
    
    // 历史数据窗口大小
    private int historyWindowSize = 100;
    
    // 指数平滑参数
    private double alpha = 0.3;
    
    // 季节性权重
    private double seasonalWeight = 0.2;
    
    private RedisLockMLPredictor() {
        // 私有构造函数
    }
    
    public static RedisLockMLPredictor getInstance() {
        return INSTANCE;
    }
    
    /**
     * 预测下一次重试的等待时间
     * @param lockKey 锁键
     * @param baseInterval 基础重试间隔
     * @param currentRetryCount 当前重试次数
     * @param globalLockCount 当前全局锁数量
     * @return 预测的等待时间
     */
    public long predictWaitTime(String lockKey, long baseInterval, int currentRetryCount, int globalLockCount) {
        if (!enabled) {
            return baseInterval;
        }
        
        try {
            LockKeyHistory history = lockKeyHistoryMap.computeIfAbsent(lockKey, k -> new LockKeyHistory());
            
            // 1. 基于锁键的历史数据预测
            double keyBasedPrediction = predictBasedOnKeyHistory(history, currentRetryCount);
            
            // 2. 基于全局负载的预测
            double loadBasedPrediction = predictBasedOnSystemLoad(globalLockCount, baseInterval);
            
            // 3. 基于重试次数的指数退避
            double retryBasedPrediction = baseInterval * Math.min(1 + Math.log10(currentRetryCount + 1), 3.0);
            
            // 4. 组合预测结果（加权平均）
            // 随着重试次数增加，更多地依赖历史数据和负载情况
            double keyWeight = Math.min(0.4, 0.1 + (currentRetryCount * 0.05));
            double loadWeight = Math.min(0.4, 0.1 + (currentRetryCount * 0.05));
            double retryWeight = 1.0 - keyWeight - loadWeight;
            
            double combinedPrediction = (keyBasedPrediction * keyWeight) + 
                                        (loadBasedPrediction * loadWeight) + 
                                        (retryBasedPrediction * retryWeight);
            
            // 确保结果在合理范围内
            long predictedInterval = Math.round(combinedPrediction);
            long minInterval = Math.max(10, baseInterval / 5); // 最小不低于基础间隔的1/5，最低10ms
            long maxInterval = baseInterval * 10; // 最大不超过基础间隔的10倍
            
            return Math.max(minInterval, Math.min(predictedInterval, maxInterval));
        } catch (Exception e) {
            logger.error("Error predicting wait time for lock {}", lockKey, e);
            // 出错时返回基础间隔
            return baseInterval;
        }
    }
    
    /**
     * 基于锁键的历史数据进行预测
     */
    private double predictBasedOnKeyHistory(LockKeyHistory history, int retryCount) {
        List<LockAttemptRecord> recentAttempts = history.getRecentAttempts();
        
        if (recentAttempts.isEmpty()) {
            return 100; // 默认值
        }
        
        // 1. 指数平滑预测
        double smoothedAvg = calculateExponentialSmoothing(recentAttempts);
        
        // 2. 考虑重试次数的影响
        List<LockAttemptRecord> similarRetryAttempts = recentAttempts.stream()
                .filter(record -> Math.abs(record.getRetryCount() - retryCount) <= 1)
                .collect(Collectors.toList());
        
        double retryAdjustedAvg = smoothedAvg;
        if (!similarRetryAttempts.isEmpty()) {
            double avgForSimilarRetries = similarRetryAttempts.stream()
                    .mapToLong(LockAttemptRecord::getWaitTime)
                    .average()
                    .orElse(smoothedAvg);
            retryAdjustedAvg = (smoothedAvg * 0.6) + (avgForSimilarRetries * 0.4);
        }
        
        // 3. 季节性调整（如果有足够数据）
        if (recentAttempts.size() >= 20) {
            double seasonalFactor = calculateSeasonalFactor(recentAttempts);
            retryAdjustedAvg *= (1.0 + (seasonalFactor * seasonalWeight));
        }
        
        return retryAdjustedAvg;
    }
    
    /**
     * 基于系统负载进行预测
     */
    private double predictBasedOnSystemLoad(int globalLockCount, long baseInterval) {
        // 根据全局锁数量动态调整间隔
        if (globalLockCount < 10) {
            return baseInterval * 0.8; // 低负载时减少等待
        } else if (globalLockCount < 50) {
            return baseInterval;
        } else if (globalLockCount < 100) {
            return baseInterval * 1.5; // 中等负载时增加等待
        } else if (globalLockCount < 200) {
            return baseInterval * 2.5; // 高负载时大幅增加等待
        } else {
            return baseInterval * 4.0; // 极高负载时显著增加等待
        }
    }
    
    /**
     * 计算指数平滑平均值
     */
    private double calculateExponentialSmoothing(List<LockAttemptRecord> records) {
        double smoothed = records.get(0).getWaitTime();
        
        for (int i = 1; i < records.size(); i++) {
            smoothed = (alpha * records.get(i).getWaitTime()) + ((1 - alpha) * smoothed);
        }
        
        return smoothed;
    }
    
    /**
     * 简单的季节性因子计算
     */
    private double calculateSeasonalFactor(List<LockAttemptRecord> records) {
        // 这里使用简单的方法：比较最近的成功率和历史平均成功率
        int recentWindow = Math.min(10, records.size() / 2);
        
        long recentSuccesses = records.subList(0, recentWindow).stream()
                .filter(LockAttemptRecord::isSuccess)
                .count();
        
        long totalSuccesses = records.stream()
                .filter(LockAttemptRecord::isSuccess)
                .count();
        
        double recentSuccessRate = (double) recentSuccesses / recentWindow;
        double overallSuccessRate = (double) totalSuccesses / records.size();
        
        // 如果最近成功率较低，说明可能需要增加等待时间
        return (overallSuccessRate - recentSuccessRate) * 0.5;
    }
    
    /**
     * 记录锁尝试结果
     */
    public void recordAttempt(String lockKey, int retryCount, long waitTime, boolean success) {
        if (!enabled) {
            return;
        }
        
        try {
            LockKeyHistory history = lockKeyHistoryMap.computeIfAbsent(lockKey, k -> new LockKeyHistory());
            history.addRecord(new LockAttemptRecord(retryCount, waitTime, success));
            
            // 更新全局历史
            globalHistory.addRecord(new LockAttemptRecord(retryCount, waitTime, success));
        } catch (Exception e) {
            logger.error("Error recording lock attempt for key {}", lockKey, e);
        }
    }
    
    /**
     * 重置历史数据
     */
    public void resetHistory() {
        lockKeyHistoryMap.clear();
        globalHistory.clear();
    }
    
    // 设置器方法
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
    
    public void setHistoryWindowSize(int historyWindowSize) {
        this.historyWindowSize = Math.max(10, historyWindowSize);
    }
    
    public void setAlpha(double alpha) {
        this.alpha = Math.max(0.1, Math.min(0.9, alpha));
    }
    
    public void setSeasonalWeight(double seasonalWeight) {
        this.seasonalWeight = Math.max(0.0, Math.min(1.0, seasonalWeight));
    }
    
    /**
     * 锁尝试记录类
     */
    private static class LockAttemptRecord {
        private final int retryCount;
        private final long waitTime;
        private final boolean success;
        private final long timestamp;
        
        public LockAttemptRecord(int retryCount, long waitTime, boolean success) {
            this.retryCount = retryCount;
            this.waitTime = waitTime;
            this.success = success;
            this.timestamp = System.currentTimeMillis();
        }
        
        public int getRetryCount() {
            return retryCount;
        }
        
        public long getWaitTime() {
            return waitTime;
        }
        
        public boolean isSuccess() {
            return success;
        }
        
        public long getTimestamp() {
            return timestamp;
        }
    }
    
    /**
     * 锁键历史数据类
     */
    private class LockKeyHistory {
        private final List<LockAttemptRecord> records = new ArrayList<>();
        
        public synchronized void addRecord(LockAttemptRecord record) {
            records.add(0, record); // 添加到开头
            
            // 保持历史数据在窗口大小内
            if (records.size() > historyWindowSize) {
                records.remove(records.size() - 1);
            }
        }
        
        public synchronized List<LockAttemptRecord> getRecentAttempts() {
            return new ArrayList<>(records);
        }
    }
    
    /**
     * 全局锁历史数据类
     */
    private class GlobalLockHistory {
        private final List<LockAttemptRecord> records = new ArrayList<>();
        
        public synchronized void addRecord(LockAttemptRecord record) {
            records.add(0, record);
            
            // 全局历史保留更多数据
            if (records.size() > (historyWindowSize * 10)) {
                records.remove(records.size() - 1);
            }
        }
        
        public synchronized void clear() {
            records.clear();
        }
    }
}